//! HttpServer module
//!
//! to start a net service for curd

pub mod utils;
use std::{collections::HashMap, convert::TryFrom, path::Path};


use magic_query::{List,QueryAble,Map,Parser}; 

use json::JsonValue; 
use path_disp::PathDisp;
//use magic_query::MagicQuery;
pub use utils::ServerUtils;

use crate::{ForceUnwrap, INDEX_KEY, MAGIC_QUERY_KEY, config::Config, data::{Data , Item, csv, reader::Reader, editor::Editor}, utils::ToJsonValue};

use nickel::{status::StatusCode, HttpRouter, QueryString};

#[derive(Debug, Serialize)]
pub struct JsonRes {
    pub msg: String,
    pub data: Vec<HashMap<String, serde_json::Value>>,
}

impl JsonRes {
    pub fn new(data: Data) -> Self {
        Self {
            msg: "success".to_string(),
            data: data.into(),
        }
    }

    pub fn msg<T: std::fmt::Display>(mut self, msg: T) -> Self {
        self.msg = format!("{}", msg);
        self
    }
}

/// implement the curd service
pub fn curd_implement(config: &Config, server: &mut nickel::Nickel) {
    for service in &config.services {
        let file = Path::new(&config.dir).resolve(&service.file);
        let url = service.url.clone();
        server_get(server, &file, &url);
        server_post(server, &file, &url);
        server_delete(server, &file, &url);
    }
}

fn server_get(server: &mut nickel::Nickel, file: &String, url: &String) {
    let url= url.clone();
    let file = file.clone();
    server.get(
        url.clone(),
        middleware! { |request|

            let mut query = HashMap::new();
            let mut msg = String::from("success");
            let mut data  = csv::from_file(&file).force_unwrap();
            let keys: Vec<String> = data[0].keys().map(|s| s.to_string()).collect();
      
            for key in keys {
                if let Some(param) = request.query().get(&key) {
                    query.insert(key.to_string(), param.to_string());
                    data = Data::from(data.find_all(key, param.to_json()));
                }
            }

            if let Some(param) = request.query().get(MAGIC_QUERY_KEY) { 
                let param = param.replace("] [", "]+[");
                if let Ok(parser) = Parser::try_from(param.as_str()) {  
                    if let Ok(list) = Into::<Map>::into(data.clone()).parse_query(&parser) {
                        data = Data::from(data.filter_list(&list).into_iter().map(|s| s.clone()).collect::<Vec<Item>>()); 
                    } else {
                        msg = String::from("bad magic query statement");
                    }             
                } else {
                    msg = String::from("bad magic query statement");
                }
                query.insert(MAGIC_QUERY_KEY.to_string(), param);
             }

            println!("GET[\"{}\"] => {:?}",  url, query);

            if  data.len() == 0 {
                msg = String::from("no result");
            }

            let json = JsonRes::new(data).msg(msg);
            serde_json::to_value(json).map_err(|e| (StatusCode::InternalServerError, e))   
        },
    );
}

fn server_post(server: &mut nickel::Nickel, file: &String, url: &String) {
    let url= url.clone();
    let file = file.clone();
    server.post(
        url.clone(),
        middleware! { |request|

            let mut query = HashMap::new();
            let mut new_data = HashMap::new();
            let mut msg = String::from("success");
            let mut data  = csv::from_file(&file).force_unwrap();
            let keys: Vec<String> = data[0].keys().map(|s| s.to_string()).collect();

            for key in &keys { 
                if let Some(param) = request.query().get(&key) {
                    query.insert(key.to_string(), param.to_string()); 
                }
                new_data.insert(key.to_string(), request.query().get(key).map_or(JsonValue::Null, |v| v.to_json()));
            }
            
            println!("POST[\"{}\"] => {:?}", url, query);

            let mut json = JsonRes::new(Data::from(vec![&new_data]));
           
            if let Some(param) = request.query().get(INDEX_KEY) {
                if let Some(index) = data.find_index(INDEX_KEY, param.to_json()) {
                    let old_val = data.index(index).unwrap();
                    for (k, v) in old_val {
                        if new_data.get(k).unwrap().is_null() {
                            new_data.insert(k.to_string(), v.clone());
                        }
                    }
                    json = JsonRes::new(Data::from(vec![&new_data]));
                    data.replace(index, new_data);
                    msg = format!("replace data at index {}", index);
                }
            } else {
                data.add(new_data);
                msg = format!("add data at index {}", data.len());
            }

            data.save_at(&file);
            serde_json::to_value(json.msg(msg)).map_err(|e| (StatusCode::InternalServerError, e))
        },
    );
}


fn server_delete(server: &mut nickel::Nickel, file: &String, url: &String) {

    let url= url.clone();
    let file = file.clone();

    server.delete(
        url.clone(),
        middleware! { |request|

            
            let msg: String;
            let mut query = HashMap::new();
            let mut removed_data = HashMap::new();
            let mut data = csv::from_file(&file).force_unwrap();
 
         
            if let Some(param) = request.query().get(INDEX_KEY) {
                query.insert(INDEX_KEY.to_string(),  param.to_string());
                if let Some(index) = data.find_index(INDEX_KEY, param.to_json()) {
                    removed_data = data.index(index).unwrap().clone();
                    msg = format!("remove data at index {}", index);
                    data.remove(index);
                } else {
                    msg = String::from("invalid index");
                }
            } else {  
                msg = String::from("missing index");
            }

            
            println!("DELETE[\"{}\"] => {:?}", url, query); 

            data.save_at(&file);
            serde_json::to_value(JsonRes::new(Data::from(vec![&removed_data])).msg(msg)).map_err(|e| (StatusCode::InternalServerError, e))
        },
    );
}